/*
在原版代码里,每一个组件都有自己的index和state数组

我们这里放在一起了

这么做有个问题, 如果某个组件中途卸载了,
重新渲染时候, 再取state就错位了

要想不错位 需要在react-dom/index.js里 组件卸载时 将对应的state也从hookStates里剔除
但... 怎么知道要卸载的组件的索引是个问题
*/

//这里存放着我们所有的状态
import PureComponent from './PureComponent';

export const hookStates = [];
//hook的索引
export let hookIndex = 0;
export let scheduleUpdate;

export function useState(initialState){
  /*hookStates[hookIndex] = hookStates[hookIndex] || (typeof initialState === 'function' ? initialState()/!*todo 应该只在组件挂载时渲染*!/ : initialState);

  const currentIndex = hookIndex; //永远都是0

  //↓不像类 不会进行状态自动合并
  function setState(newState){
    if (typeof newState === 'function'){ //← 函数式更新 传入一个最新的state
      newState = newState(hookStates[currentIndex]);
    }

    hookStates[currentIndex] = newState;
    scheduleUpdate(); //当状态改变后要重新更新应用
  }

  return [hookStates[hookIndex++],setState];*/

  return useReducer(null, initialState); //useState 是 useReducer的语法糖, 无需编写reducer, 但需要自己计算好状态再传过去
}

export function useReducer(reducer,initialState){
  hookStates[hookIndex] = hookStates[hookIndex] || (typeof initialState === 'function' ? initialState()/*todo 应该只在组件挂载时渲染*/ : initialState);

  const currentIndex = hookIndex; //永远都是0

  //↓不像类 不会进行状态自动合并
  function dispatch(action){
    const lastState = hookStates[currentIndex];

    const nextState = reducer ?
      reducer(lastState, action) :
      (typeof action === 'function'? action(lastState)/*←函数式更新*/:action);

    // 浅比较不一样才会触发更新
    if (lastState !== nextState) {
      hookStates[currentIndex] = nextState;

      scheduleUpdate(); //当状态改变后要重新更新应用
    }
  }

  return [hookStates[hookIndex++],dispatch];
}

export function useMemo(factory,deps){
  if(hookStates[hookIndex]){
    const [lastMemo, lastDeps] = hookStates[hookIndex];
    const same = deps.every((item, index) => item === lastDeps[index]);
    if (same) {
      hookIndex++;
      return lastMemo;
    }
  }

  const newMemo = factory()
  hookStates[hookIndex++] = [newMemo, deps];
  return newMemo;
}

export function useCallback(callback,deps){
  if (hookStates[hookIndex]) {
    const [lastCallback, lastDeps] = hookStates[hookIndex];
    const same = deps.every((item, index) => item === lastDeps[index]);
    if (same) {
      hookIndex++;
      return lastCallback;
    }
  }
  hookStates[hookIndex++] = [callback, deps];
  return callback;
}

export function useContext(context){
  return context._currentValue;
}

/**
 *
 * @param callback 回调函数, 页面渲染完后执行
 * @param dependencies 回调函数是否需要重新执行所依赖的数组
 */
export function useEffect(callback,dependencies){
  /*if (hookStates[hookIndex]) {
    const [destroyFunction, lastDependencies] = hookStates[hookIndex];
    const allTheSame = dependencies && dependencies.every((item, index) => item === lastDependencies[index]); //如果是空数组 ---> [], .every 会返回true

    if(allTheSame){ //依赖没有变化
      return hookIndex++;
    }else {
      destroyFunction && destroyFunction();
    }
  }

  const currentIndex = hookIndex;
  hookIndex++

  //第一次渲染 或则 依赖变化了, 重新执行effect
  setTimeout/!*← 使用宏任务, 确保在渲染后执行(微任务是在渲染前)*!/(() => {
    const destroyFunction = callback();
    hookStates[currentIndex] = [destroyFunction, dependencies];
  });*/

  runUseXxEffect(callback, dependencies)(setTimeout);
}

export function useLayoutEffect(callback,dependencies){
  runUseXxEffect(callback, dependencies)(queueMicrotask);
}

function runUseXxEffect(callback,dependencies){
  return (AsyncFunction)=>{
    if (hookStates[hookIndex]) {
      const [destroyFunction, lastDependencies] = hookStates[hookIndex];
      const allTheSame = dependencies && dependencies.every((item, index) => item === lastDependencies[index]); //如果是空数组 ---> [], .every 会返回true

      if(allTheSame){ //依赖没有变化
        return hookIndex++;
      }else {
        destroyFunction && destroyFunction();
      }
    }

    const currentIndex = hookIndex;
    hookIndex++

    AsyncFunction(() => {
      const destroyFunction = callback();
      hookStates[currentIndex] = [destroyFunction, dependencies];
    })
  }
}

export function useRef(initialState){
  hookStates[hookIndex] = hookStates[hookIndex] || {current: initialState};

  return hookStates[hookIndex++];
}

export function useImperativeHandle(ref,factory){
  ref.current = factory();
}
